(function () {
  // 映射类型的主要作用即是基于键名映射到键值类型。

  type Stringify<T> = {
    [K in keyof T]: string;
  }

  //这个工具类型会接受一个对象类型（假设我们只会这么用），使用 keyof 获得这个对象类型的键名组成字面量联合类型，然后通过映射类型（即这里的 in 关键字）将这个联合类型的每一个成员映射出来，并将其键值类型设置为 string。

  //具体使用的表现是这样的：
  interface Foo_0 {
    prop1: string;
    prop2: number;
    prop3: boolean;
    prop4: () => void;
  }
  type StringifiedFoo = Stringify<Foo_0>

  // 等价于
  /*
   interface StringifiedFoo {
     prop1: string;
     prop2: string;
     prop3: string;
     prop4: string;
   }
 */

  //我们应该很少会需要把一个接口的所有属性类型映射到 string？这有什么意义吗？别忘了，既然拿到了键，那键值类型其实也能拿到：

  type Clone<T> = {
    [K in keyof T]: T[K]
  }
  // 这里的T[K]其实就是上面说到的索引类型访问，我们使用键的字面量类型访问到了键值的类型，这里就相当于克隆了一个接口。需要注意的是，这里其实只有K in 属于映射类型的语法，keyof T 属于 keyof 操作符，[K in keyof T]的[]属于索引签名类型，T[K]属于索引类型访问。
  type CloneFoo_0 = Clone<Foo_0>


}())